package com.woniu.service.impl;


import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniu.commons.exception.YogaException;
import com.woniu.commons.model.RedisKey;
import com.woniu.commons.util.DigestUtil;
import com.woniu.commons.util.RedisUtil;
import com.woniu.dao.YCoachMapper;
import com.woniu.dao.YCoachResumeMapper;
import com.woniu.dao.YCoachTypeMapper;
import com.woniu.model.dto.CoachDto;
import com.woniu.model.dto.CoachPageDto;
import com.woniu.model.entity.*;
import com.woniu.model.param.*;
import com.woniu.service.YCoachService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.logging.log4j.util.Base64Util;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Base64;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 胡歌
 * @since 2024年05月22日
 */
@Service
public class YCoachServiceImpl extends ServiceImpl<YCoachMapper, YCoach> implements YCoachService {
    @Resource
    private RedissonClient redissonClient;//通过这个完成释放锁上锁等一系列操作，导入依赖后直接使用
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private YCoachMapper coachMapper;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private YCoachTypeMapper coachTypeMapper;
    @Resource
    private YCoachResumeMapper coachResumeMapper;

    @Override
    public void sendRegisterCode(String mobile) {
        //判断手机号码是否在一分钟内是否发送短信
        if (redisUtil.exist(RedisKey.registerCodeOnMinute(mobile))) {
            throw new YogaException(500,"1分钟不能再次发送");
        }
        //判断该手机号码在一小时内的短信发送次数
        if (redisUtil.exist(RedisKey.sendMessageNumber(mobile))) {
            Integer number = Integer.parseInt(redisUtil.get(RedisKey.sendMessageNumber(mobile)));
            if (number >= 5) {
                throw new YogaException(500,"超过发送短信次数,请稍后再试");
            }
        }
        //判断手机号是否注册过
        QueryWrapper<YCoach> wrapper = new QueryWrapper<>();
        wrapper.eq("coach_phone", mobile);

        YCoach coachInfo = coachMapper.selectOne(wrapper);

        if (coachInfo != null) {
            throw new YogaException(500,"该手机号已注册");
        }
        //将手机号码保存到消息队列中
        rabbitTemplate.convertAndSend("register_phone_queue",mobile);
    }

    @Override
    public void CodeCoachRegister(CodeCoachRegisterParam param) throws Exception {
        //创建一把手机号码锁(公平锁)
        RLock phoneLock = redissonClient.getFairLock(RedisKey.phoneLock(param.getCoachPhone()));
        //创建一把用户名锁（公平锁）
        RLock userNameLock = redissonClient.getFairLock(RedisKey.userNameLock(param.getCoachName()));
        //变成多资源锁；这里放进该方法的顺序就是加锁的顺序
        RLock lock = redissonClient.getMultiLock(phoneLock, userNameLock);
        if (lock.tryLock(10, TimeUnit.SECONDS)) {
            //设置10秒；代表成功
            try {//一定要用try块包括成功，finally成功与否都要释放锁
                //首先判断总错误次数
                if (redisUtil.exist(RedisKey.codeErrorNumber(param.getCoachPhone()))) {
                    if (Integer.parseInt(redisUtil.get(RedisKey.codeErrorNumber(param.getCoachPhone()))) >= 5) {
                        throw new Exception("超过最大次数限制，验证码失效");
                    }
                }
                //先判断验证码是否正确
                if (!param.getCode().equals(redisUtil.get(RedisKey.registerCodeKey(param.getCoachPhone())))) {
                    //验证码错误，就给他的错误总次数+1
                    if (redisUtil.exist(RedisKey.codeErrorNumber(param.getCoachPhone()))) {
                        redisUtil.incr(RedisKey.codeErrorNumber(param.getCoachPhone()));
                        if (Integer.parseInt(redisUtil.get(RedisKey.codeErrorNumber(param.getCoachPhone()))) >= 5) {
                            redisUtil.delete(RedisKey.registerCodeKey(param.getCoachPhone()));
                        }
                    } else {
                        redisUtil.set(RedisKey.codeErrorNumber(param.getCoachPhone()), "1", 5 * 60);
                    }
                    throw new Exception("验证码错误");
                }
                //判断手机号是否注册过
                QueryWrapper<YCoach> wrapper = new QueryWrapper<YCoach>();
                wrapper.eq("coach_phone", param.getCoachPhone());

                YCoach coachInfo = coachMapper.selectOne(wrapper);
                if (coachInfo != null) {
                    throw new Exception("该手机号已注册");
                }
                //判断用户名是否已经被注册
                QueryWrapper<YCoach> wrapper1 = new QueryWrapper<YCoach>();
                wrapper1.eq("coach_name", param.getCoachName());
                YCoach coachInfo1 = coachMapper.selectOne(wrapper1);
                if (coachInfo1 != null) {
                    throw new Exception("教练名已注册");
                }
                //执行注册
                coachInfo = BeanUtil.toBean(param, YCoach.class);
                //对密码加密
                String md5 = DigestUtil.md5(param.getCoachPassword(), param.getCoachName());
                coachInfo.setCoachPassword(md5);
                coachInfo.setCoachState(3);
                coachMapper.insert(coachInfo);
                //删除验证码
                redisUtil.delete(RedisKey.registerCodeKey(param.getCoachPhone()));


                //在简历表里面去新增一个姓名
                YCoachResume coachResume = new YCoachResume();
                coachResume.setCoachId(coachInfo.getCoachId());
                coachResume.setName(param.getCoachName());
                coachResume.setPhone(param.getCoachPhone());
                coachResumeMapper.insert(coachResume);


            } finally {
                lock.unlock();//释放锁
            }

        } else {
            //代表失败
            throw new Exception("服务器繁忙");
        }

    }

    @Override
    public void LoginCodePhone(String mobile) throws Exception {
        if (redisUtil.exist(RedisKey.loginCodeOnMinuteKey(mobile))) {
            throw new YogaException(5001,"1分钟不能再次发送");
        }
        if (redisUtil.exist(RedisKey.sendMessageNumber(mobile))) {
            int num = Integer.parseInt(redisUtil.get(RedisKey.sendMessageNumber(mobile)));
            if (num >= 20) {
                throw new YogaException(500,"超过发送短信次数,请稍后再试");
            }
        }
        QueryWrapper<YCoach> wrapper = new QueryWrapper<YCoach>();
        wrapper.eq("coach_phone", mobile);
        YCoach coach = coachMapper.selectOne(wrapper);
        if (coach == null) {
            throw new YogaException(5002,"手机号尚未注册");
        }
        //将手机号存入消息队列
        rabbitTemplate.convertAndSend("login_code_queue",mobile);
    }

    @Override
    public CoachDto codeLogin(CoachCodeLoginParam param) throws Exception {
        //如果存在错误次数的键 判断次数是否大于5
        if (redisUtil.exist(RedisKey.codeErrorNumber(param.getCoachPhone()))) {
            if ((Integer.parseInt(redisUtil.get(RedisKey.codeErrorNumber(param.getCoachPhone())))) >= 5) {
                throw new YogaException(5003,"验证码错误次数达到上限");
            }
        }
        //传过来的验证码如果不等于redis存入的验证码
        if (!param.getCode().equals(redisUtil.get(RedisKey.loginCodeKey(param.getCoachPhone())))) {
            //存在输入验证码错误次数限制
            if (redisUtil.exist(RedisKey.codeErrorNumber(param.getCoachPhone()))) {
                //输入验证码错误次数加1
                redisUtil.incr(RedisKey.codeErrorNumber(param.getCoachPhone()));
                //输入验证码错误次数5 删除redis中的验证码 无效了
                if ((Integer.parseInt(redisUtil.get(RedisKey.codeErrorNumber(param.getCoachPhone())))) >= 5) {
                    redisUtil.delete(RedisKey.loginCodeKey(param.getCoachPhone()));
                }

            } else {
                //如果不存在输入验证码错误次数限制 添加一个
                redisUtil.set(RedisKey.codeErrorNumber(param.getCoachPhone()), "1", 5 * 60);
            }
            throw new YogaException(5005,"验证码错误");
        }
        QueryWrapper<YCoach> wrapper = new QueryWrapper<YCoach>();
        wrapper.eq("coach_phone", param.getCoachPhone());
        YCoach coach = coachMapper.selectOne(wrapper);
        if (coach == null) {
            throw new YogaException(5006,"手机号不存在");
        }
        CoachDto dto = new CoachDto();
        dto.setCoachName(coach.getCoachName());
        dto.setId(coach.getCoachId());
        return dto;

    }

    @Override
    public CoachDto coachLogin(coachLoginParam param) throws Exception {
        QueryWrapper<YCoach> wrapper = new QueryWrapper<YCoach>();
        wrapper.eq("coach_name", param.getCoachName());
        YCoach coach = coachMapper.selectOne(wrapper);
        if (coach == null) {
            throw new YogaException(5002,"尚未注册");
        }
        if (!coach.getCoachPassword().equals(DigestUtil.md5(param.getCoachPassword(), param.getCoachName()))) {
            throw new YogaException(5005,"密码错误");
        }
        if(coach.getCoachState()==0){
            throw new YogaException(5006,"账号已注销 请联系管理员");
        }
        CoachDto dto = new CoachDto();
        dto.setId(coach.getCoachId());
        dto.setCoachName(coach.getCoachName());
        return dto;
    }

    @Override
    public void updateImage(UpdateCoachImageParam param) {
        YCoach coach = coachMapper.selectById(param.getCoachId());
        coach.setCoachImage(param.getImage());
        coachMapper.updateById(coach);
    }

    @Override
    public YCoach updateMessage(UpdateMessageParam param) {
        YCoach coach = new YCoach();
        coach.setCoachNickName(param.getCoachNickName());
        coach.setCoachId(param.getCoachId());
        coachMapper.updateById(coach);
        return coach;
    }

    @Override
    public void UpdateCoachPassword(UpdateCoachPasswordParam param) throws Exception {
        YCoach coach = coachMapper.selectById(param.getCoachId());
        String newPass=DigestUtil.md5(param.getNewPassword(),coach.getCoachName());
        System.out.println(coach.getCoachPassword());
        System.out.println(newPass);
        if (coach.getCoachPassword().equals(newPass)){
            throw new Exception("两次密码一样，请重新输入");
        }
        coach.setCoachPassword(newPass);
        coachMapper.updateById(coach);
    }

    @Override
    public YCoach updateCoach(AdminUpdateCoachStateParam param) {
        YCoach coach = new YCoach();
        coach.setCoachId(param.getCoachId());
        coach.setCoachState(param.getCoachState());
        coachMapper.updateById(coach);
        return coach;
    }

    @Override
    public YCoach updateCoachState(UpdateCoachStateParam param) {
        YCoach coach = new YCoach();
        coach.setCoachId(param.getCoachId());
        coach.setCoachState(param.getCoachState());
        coachMapper.updateById(coach);
        return coach;
    }

    @Override
    public YCoach updateCoachTypeId(updateCoachTypeIdParam param) {
        YCoach coach = new YCoach();
        coach.setCoachId(param.getCoachId());
        coach.setCoachTypeId(param.getCoachTypeId());
        coachMapper.updateById(coach);
        return coach;
    }

    @Override
    public void UpdateCoachPhone(String mobile) {
        //判断手机号码是否在一分钟内是否发送短信
        if (redisUtil.exist(RedisKey.updateCodeOnMinute(mobile))) {
            throw new YogaException(500,"1分钟不能再次发送");
        }
        //判断该手机号码在一小时内的短信发送次数
        if (redisUtil.exist(RedisKey.updateMessageNumber(mobile))) {
            Integer number = Integer.parseInt(redisUtil.get(RedisKey.updateMessageNumber(mobile)));
            if (number >= 50) {
                throw new YogaException(500,"超过发送短信次数,请稍后再试");
            }
        }
        //判断手机号是否注册过
        QueryWrapper<YCoach> wrapper = new QueryWrapper<>();
        wrapper.eq("coach_phone", mobile);

        YCoach coach = coachMapper.selectOne(wrapper);

        if (coach == null) {
            throw new YogaException(600,"手机号不匹配");
        }
        //将手机号码保存到消息队列中
        rabbitTemplate.convertAndSend("update_phone_queue",mobile);
    }

    @Override
    public void codeUpdate(CodeCoachUpdateParam param) throws Exception {

        //创建一把手机号码锁(公平锁)
        RLock phoneLock = redissonClient.getFairLock(RedisKey.phoneLock(param.getOldPhone()));
        //变成多资源锁；这里放进该方法的顺序就是加锁的顺序
        RLock lock = redissonClient.getMultiLock(phoneLock);

        if (lock.tryLock(10, TimeUnit.SECONDS)) {
            //设置10秒；代表成功
            try {//一定要用try块包括成功，finally成功与否都要释放锁
                //首先判断总错误次数
                if (redisUtil.exist(RedisKey.codeErrorNumber(param.getOldPhone()))) {
                    if (Integer.parseInt(redisUtil.get(RedisKey.codeErrorNumber(param.getOldPhone()))) >= 5) {
                        throw new Exception("超过最大次数限制，验证码失效");
                    }
                }
                //先判断验证码是否正确
                if (!param.getCode().equals(redisUtil.get(RedisKey.updateCodeKey(param.getOldPhone())))) {
                    //验证码错误，就给他的错误总次数+1
                    if (redisUtil.exist(RedisKey.codeErrorNumber(param.getOldPhone()))) {
                        redisUtil.incr(RedisKey.codeErrorNumber(param.getOldPhone()));
                        if (Integer.parseInt(redisUtil.get(RedisKey.codeErrorNumber(param.getOldPhone()))) >= 5) {
                            redisUtil.delete(RedisKey.updateCodeKey(param.getOldPhone()));
                        }
                    } else {
                        redisUtil.set(RedisKey.codeErrorNumber(param.getOldPhone()), "1", 5 * 60);
                    }
                    throw new Exception("验证码错误");
                }
                //判断手机号是否注册过
                QueryWrapper<YCoach> wrapper = new QueryWrapper<YCoach>();
                wrapper.eq("coach_phone", param.getOldPhone());

                YCoach coach = coachMapper.selectOne(wrapper);
                if (coach == null) {
                    throw new Exception("该手机号有误");
                }
                //执行修改
                coach = BeanUtil.toBean(param, YCoach.class);
                coach.setCoachPhone(param.getNewPhone());
                int update = coachMapper.update(coach, wrapper);
                if (update==0){
                    throw new Exception("手机号更新失败");
                }
                //删除验证码
                redisUtil.delete(RedisKey.registerCodeKey(param.getOldPhone()));
            } finally {
                lock.unlock();//释放锁
            }
        } else {
            //代表失败
            throw new Exception("服务器繁忙");
        }
    }

    @Override
    public Page<CoachPageDto> loadPageCoach(LoadPageCoachParam param) throws YogaException {
        Page<YCoach> page = new Page<>(param.getPageNum(), param.getPageSize());
        QueryWrapper<YCoach> wrapper = new QueryWrapper<>();
        if (ObjectUtils.isNotEmpty(param.getCoachName())) {
            wrapper.like("coach_name", param.getCoachName());
        }
        Page<YCoach> yCoachPage = coachMapper.selectPage(page, wrapper);


        Page<CoachPageDto> page1 = new Page<>();
        page1.setCurrent(yCoachPage.getCurrent());
        page1.setOptimizeCountSql(yCoachPage.optimizeCountSql());
        page1.setPages(yCoachPage.getPages());
        page1.setSearchCount(yCoachPage.searchCount());
        page1.setSize(yCoachPage.getSize());
        page1.setTotal(yCoachPage.getTotal());

        ArrayList<CoachPageDto> dto = new ArrayList<>();

        for (YCoach coach:yCoachPage.getRecords()){
            CoachPageDto pageDto = BeanUtil.toBean(coach, CoachPageDto.class);

            Integer coachTypeId = coach.getCoachTypeId();
            if (coachTypeId!=null){
                YCoachType yCoachType = coachTypeMapper.selectById(coachTypeId);
                String coachTypeName = (yCoachType != null) ? yCoachType.getCoachTypeName() : "待分配";
                pageDto.setCoachTypeName(coachTypeName);
            }else {
                pageDto.setCoachTypeName("待分配");
            }
            dto.add(pageDto);
        }
        page1.setRecords(dto);
        return page1;
    }

    @Override
    public YCoach findCoachInfo(Integer coachId) {
        YCoach coach = coachMapper.selectById(coachId);
        if (coach==null){
            throw new YogaException(66666,"教练不存在");
        }
        return coach;
    }

    /**
     * 通过token'载入教练详细
     * @param coachId
     * @return
     */
    @Override
    public YCoach loadCoachByToken(Integer coachId) {

        return baseMapper.selectById(coachId);
    }

    /**
     * 完善教练端的其他信息
     * @param param
     * @return
     * @throws YogaException
     */
    @Override
    public void updateCoachBase(UpdateCoachBaseParam param) throws YogaException {
        YCoach coach = baseMapper.selectById(param.getCoachId());
        if (coach==null){
            throw new YogaException(500,"教练不存在");
        }
        if (coach.getCoachState()==0){
            throw new YogaException(500,"不存在该教练");
        }
        coach.setCoachPersonal(param.getCoachPersonal());
        coach.setCoachTypeId(param.getCoachTypeId());
        coach.setCoachRemark(param.getCoachRemark());

        baseMapper.updateById(coach);
    }


}
